package org.fjsei.yewu.config.datasource;

import org.fjsei.yewu.aop.DatabaseClusterMonitor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLTimeoutException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class CockroachDBHealthIndicator implements HealthIndicator {

    private final DataSource dataSource;
    private final ApplicationEventPublisher eventPublisher;
    private DatabaseClusterMonitor clusterMonitor; // 移除了final
    private volatile boolean clusterHealthy = true;
    private volatile boolean maintenanceMode = false;
    private volatile int consecutiveHealthyChecks = 0;
    private static final int RECOVERY_THRESHOLD = 2;

    public CockroachDBHealthIndicator(@Qualifier("seiDataSource") DataSource dataSource,
                                      ApplicationEventPublisher eventPublisher) {
        this.dataSource = dataSource;
        this.eventPublisher = eventPublisher;
        // 注意：clusterMonitor 不在此处初始化
    }

    // Setter注入，打破循环依赖
    @Autowired
    public void setClusterMonitor(DatabaseClusterMonitor clusterMonitor) {
        this.clusterMonitor = clusterMonitor;
    }

    @Override
    public Health health() {
        // 如果是维护模式，检查是否应该自动恢复
        if (maintenanceMode) {
            return checkRecoveryPossibility();
        }

        try (Connection connection = dataSource.getConnection();
             Statement statement = connection.createStatement()) {

            statement.setQueryTimeout(5);
            ResultSet rs = statement.executeQuery("SELECT a.* FROM public.authority AS a WHERE name='Master' LIMIT 1");

            if (rs.next()) {
                // 数据库健康
                handleHealthyState();
                return Health.up()
                        .withDetail("message", "CockroachDB cluster is healthy")
                        .withDetail("database", "CockroachDB")
                        .withDetail("consecutiveChecks", consecutiveHealthyChecks)
                        .build();
            }
        } catch (Exception e) {
            // 数据库不健康
            handleUnhealthyState(e);
            return Health.down(e)
                    .withDetail("database", "CockroachDB")
                    .withDetail("error", e.getMessage())
                    .build();
        }

        return Health.down()
                .withDetail("message", "CockroachDB health check failed")
                .build();
    }

    private void handleHealthyState() {
        if (!clusterHealthy) {
            clusterHealthy = true;
            consecutiveHealthyChecks++;
//            logger.info("CockroachDB cluster recovered, consecutive checks: {}", consecutiveHealthyChecks);

            // 发布恢复事件
            eventPublisher.publishEvent(new DatabaseHealthEvent(this, true, null));
        } else {
            consecutiveHealthyChecks++;
        }

        // 检查是否应该触发自动恢复
        checkAutoRecovery();
    }

    private void handleUnhealthyState(Exception error) {
        if (clusterHealthy) {
            clusterHealthy = false;
            consecutiveHealthyChecks = 0;
//            logger.warn("CockroachDB health check failed: {}", error.getMessage());

            // 发布故障事件
            eventPublisher.publishEvent(new DatabaseHealthEvent(this, false, error));
        }
    }

    private Health checkRecoveryPossibility() {
        try (Connection connection = dataSource.getConnection();
             Statement statement = connection.createStatement()) {

            statement.setQueryTimeout(5);
            ResultSet rs = statement.executeQuery("SELECT a.* FROM public.authority AS a WHERE name='Master' LIMIT 1");

            if (rs.next()) {
                // 数据库已恢复，但仍在维护模式
                handleHealthyState();
                return Health.down()
                        .withDetail("message", "CockroachDB cluster is healthy but in maintenance mode")
                        .withDetail("action", "Auto-recovery in progress")
                        .withDetail("consecutiveChecks", consecutiveHealthyChecks)
                        .withDetail("recoveryThreshold", RECOVERY_THRESHOLD)
                        .build();
            }
        } catch (Exception e) {
            handleUnhealthyState(e);
        }

        return Health.down()
                .withDetail("message", "CockroachDB cluster is in maintenance mode")
                .withDetail("action", "Service temporarily unavailable")
                .build();
    }

    private void checkAutoRecovery() {
        if (maintenanceMode && consecutiveHealthyChecks >= RECOVERY_THRESHOLD && clusterMonitor != null) {
//            logger.info("Auto-recovery conditions met: {} consecutive healthy checks", consecutiveHealthyChecks);
            clusterMonitor.disableMaintenanceMode();
            consecutiveHealthyChecks = 0;
            maintenanceMode = false;
        }
    }

    // 自定义事件类
    public static class DatabaseHealthEvent {
        private final Object source;
        private final boolean healthy;
        private final Exception error;

        public DatabaseHealthEvent(Object source, boolean healthy, Exception error) {
            this.source = source;
            this.healthy = healthy;
            this.error = error;
        }

        public boolean isHealthy() { return healthy; }
        public Exception getError() { return error; }
        public Object getSource() { return source; }
    }

    public void setClusterHealthy(boolean healthy) {
        this.clusterHealthy = healthy;
        if (!healthy) {
            consecutiveHealthyChecks = 0;
        }
    }

    public void setMaintenanceMode(boolean maintenanceMode) {
        this.maintenanceMode = maintenanceMode;
        if (maintenanceMode) {
            consecutiveHealthyChecks = 0;
        }
    }

    public int getConsecutiveHealthyChecks() {
        return consecutiveHealthyChecks;
    }

    public boolean isRecoveryPending() {
        return maintenanceMode && consecutiveHealthyChecks > 0;
    }
}
